package com.personal.datacompare.api;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

import com.personal.core.bean.TwoTuple;
import com.personal.core.data.DataColumn;
import com.personal.core.data.DataRow;
import com.personal.core.data.DataTables;
import com.personal.core.data.DataTable;
import com.personal.core.utils.Assert;
import com.personal.core.utils.CoreUtil;
import com.personal.core.utils.FileUtil;
import com.personal.core.utils.Remind;
import com.personal.core.utils.StringUtil;
import com.personal.datacompare.config.ColumnConfig;
import com.personal.datacompare.config.CompareDataTableConfig;
import com.personal.datacompare.config.CompareDataTableConfigApp;
import com.personal.datacompare.config.TransRowColConfig;
import com.personal.datacompare.config.TransRowColConfig.TransColConfig;
import com.personal.datacompare.config.TransRowColConfig.TransRowConfig;
import com.personal.datacompare.factory.CompareTreeFactory;
import com.personal.datacompare.factory.SimilarityEnum;
import com.personal.datacompare.tree.AbstractCompareTree;
import com.personal.datacompare.tree.CompareTreeNode;
import com.personal.dataconvert.DataSet2Excel;
import com.personal.dataconvert.DataTable2Html;
import com.personal.dataconvert.bean.CombineColumnConfig;
import com.personal.dataconvert.util.ExcelHtmlUtil;

/**
 * 复杂对比合并DataTable 构造器较多。应使用静态工厂方法获取
 * @author cuibo
 */
public class CompareDataTable
{
    public static String JSFUNC_OPENDIFFREASON = "openWriteDiffReason";
    public static int 差异原因宽度 = 150;
    public static String 差异原因样式 = "diffreasonclass";
    public static String 差异原因唯一标识 = "ID";
    public static String 对比行是否存在差率超过设置的差率标记 = "comparedatatable_datarow_passdiffpercentflag";
    public static String 对比行是否存在对比列不一致的标记 = "comparedatatable_datarow_havediffflag";
    public static String 对比行是否存在未匹配上的标记 = "comparedatatable_datarow_havenomatchflag";
    public static String 对比行是否是插入行的标记 = "comparedatatable_datarow_insertflag";
    public static String 操作 = "操作";
    public static int 操作宽度 = 50;
    public static String 操作值 = "分析";
    public static String 多列名表名分隔符 = "or";
    public static String 差异原因标记列分隔符 = ",";
    public static boolean 对比列添加字母标记 = true;
    public static String 差值 = "差值";
    public static String 差率 = "差率";
    public static String 合计 = "合计";
    
    static
    {
        try
        {
            CompareDataTable.setConfigProperties(CompareDataTable.class.getResourceAsStream("compare.properties"));
        } catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 设置对比配置
     * @param in
     * @throws IOException
     */
    public static void setConfigProperties(InputStream in)
    {
        Properties properties = new Properties();
        try
        {
            properties.load(in);
        } catch (Exception e)
        {
        } finally
        {
            FileUtil.release(in);
        }
        if (!CoreUtil.isEmpty(properties.get("JSFUNC_OPENDIFFREASON")))
        {
            CompareDataTable.JSFUNC_OPENDIFFREASON = properties.getProperty("JSFUNC_OPENDIFFREASON");
        }
        if (!CoreUtil.isEmpty(properties.get("DIFFREASONCOLWIDTH")))
        {
            CompareDataTable.差异原因宽度 = CoreUtil.parseInt(properties.getProperty("DIFFREASONCOLWIDTH"));
        }
        if (!CoreUtil.isEmpty(properties.get("DIFFREASONPRIMARYKEY")))
        {
            CompareDataTable.差异原因唯一标识 = properties.getProperty("DIFFREASONPRIMARYKEY");
        }
        if (!CoreUtil.isEmpty(properties.get("DIFFREASONOPERATECOLNAME")))
        {
            CompareDataTable.操作 = properties.getProperty("DIFFREASONOPERATECOLNAME");
        }
        if (!CoreUtil.isEmpty(properties.get("DIFFREASONOPERATELINKNAME")))
        {
            CompareDataTable.操作值 = properties.getProperty("DIFFREASONOPERATELINKNAME");
        }
        if (!CoreUtil.isEmpty(properties.get("DIFFREASONOPERATECOLWIDTH")))
        {
            CompareDataTable.操作宽度 = CoreUtil.parseInt(properties.getProperty("DIFFREASONOPERATECOLWIDTH"));
        }
        if (!CoreUtil.isEmpty(properties.get("DIFFVALUECOLNAME")))
        {
            CompareDataTable.差值 = properties.getProperty("DIFFVALUECOLNAME");
        }
        if (!CoreUtil.isEmpty(properties.get("DIFFPERCENTCOLNAME")))
        {
            CompareDataTable.差率 = properties.getProperty("DIFFPERCENTCOLNAME");
        }
        if (!CoreUtil.isEmpty(properties.get("DIFFREASONCLASS")))
        {
            CompareDataTable.差异原因样式 = properties.getProperty("DIFFREASONCLASS");
        }
        if (!CoreUtil.isEmpty(properties.get("MULTICOLUMNTABLENAMESPLIT")))
        {
            CompareDataTable.多列名表名分隔符 = properties.getProperty("MULTICOLUMNTABLENAMESPLIT");
        }
        if (!CoreUtil.isEmpty(properties.get("DIFFREASONCOLKEY")))
        {
            CompareDataTable.差异原因标记列分隔符 = properties.getProperty("DIFFREASONCOLKEY");
        }
        if (!CoreUtil.isEmpty(properties.get("COMPARECOLUMNADDCHARACTER")))
        {
            CompareDataTable.对比列添加字母标记 = CoreUtil.parseBoolean(properties.getProperty("COMPARECOLUMNADDCHARACTER"));
        }
        if (!CoreUtil.isEmpty(properties.get("TOTALCOLUMNNAME")))
        {
            CompareDataTable.合计 = properties.getProperty("TOTALCOLUMNNAME");
        }
    }
    
    /** 配置ID */
    private String configId;
    /** 对比合并配置 */
    private CompareDataTableConfig tableConfig;
    /** 合并配置集合 */
    private List<DataTable> tables;
    /** 差异原因Table */
    private DataTable diffReasonTable;
    /** 是否已经合并对比 */
    private volatile boolean hasCompare = false;
    /** 合并对比结果 */
    private DataTable resultTable;
    /** 合并对比结果集 */
    private Map<CompareDataTableConfig, DataTable> resultTables;
    /** 合并对比结果树 */
    private AbstractCompareTree resultTree;
    /** 同名缓存 */
    private Map<String, String> sameNameCache;
    /** 配置流 */
    private InputStream in;
    /** 配置Map */
    private Map<String, CompareDataTableConfig> configMap;
    /** 数据Map key:configId */
    private Map<String, List<DataTable>> tableMap;
    /** 差异原因Map key：configId */
    private Map<String, DataTable> diffReasonTableMap;
    /** 对比结果和差异原因DataTable匹配上的所有节点 */
    private Map<CompareTreeNode, CompareTreeNode> diffMatchNodes;
    /** 是否将差异原因的字段转成对比字段 */
    private boolean transDiffCol2CompareCol;
    /** 手动注入对比实现 */
    private Map<String, List<AbstractCompareTree>> trees;
    /** 合并配置  */
    private Map<String, Set<? extends CombineColumnConfig>> combineMap;
    /** 是否是操作多表 */
    private boolean multiOperate = false;
    
    /**
     * 普通对比
     * @param tableConfig
     * @param tables
     */
    public CompareDataTable(CompareDataTableConfig tableConfig, DataTable... tables)
    {
        this(tableConfig, null, false, tables);
    }

    /**
     * 带有差异原因的对比
     * @param tableConfig
     * @param diffReasonTable
     * @param transDiffCol2CompareCol 是否将差异原因的字段转成对比字段
     * @param tables
     */
    public CompareDataTable(CompareDataTableConfig tableConfig, DataTable diffReasonTable,
            boolean transDiffCol2CompareCol, DataTable... tables)
    {
        super();
        this.tableConfig = tableConfig;
        this.diffReasonTable = diffReasonTable;
        this.transDiffCol2CompareCol = transDiffCol2CompareCol;
        this.tables = new ArrayList<DataTable>();
        if (tables != null && tables.length > 0)
        {
            for (DataTable dataTable : tables)
            {
                this.tables.add(dataTable);
            }
        }
    }

    /***
     * @param in 配置流
     * @param diffReasonTableMap
     * @param transDiffCol2CompareCol 是否将差异原因的字段转成对比字段
     * @param tables 数据 key：每个对比的ID value: 每个对比的对比数据
     */
    public CompareDataTable(InputStream in, Map<String, DataTable> diffReasonTableMap, boolean transDiffCol2CompareCol,
            Map<String, List<DataTable>> tableMap)
    {
        super();
        this.in = in;
        this.diffReasonTableMap = diffReasonTableMap;
        this.transDiffCol2CompareCol = transDiffCol2CompareCol;
        this.tableMap = tableMap;
    }

    public CompareDataTable(InputStream in, Map<String, List<DataTable>> tableMap)
    {
        this(in, null, false, tableMap);
    }

    /***
     * 传入配置流的单个普通对比
     * @param in 配置流
     * @param configId 配置ID(配置流中的某个ID)
     * @param tables 数据
     */
    public CompareDataTable(InputStream in, String configId, DataTable... tables)
    {
        this(in, configId, null, false, tables);
    }

    /**
     * 传入配置流的带有差异原因的对比
     * @param in
     * @param configId
     * @param diffDataTable
     * @param transDiffCol2CompareCol 是否将差异原因的字段转成对比字段
     * @param tables
     */
    public CompareDataTable(InputStream in, String configId, DataTable diffDataTable, boolean transDiffCol2CompareCol,
            DataTable... tables)
    {
        super();
        this.in = in;
        this.configId = configId;
        diffReasonTable = diffDataTable;
        this.transDiffCol2CompareCol = transDiffCol2CompareCol;
        this.tables = new ArrayList<DataTable>();
        if (tables != null && tables.length > 0)
        {
            for (DataTable dataTable : tables)
            {
                this.tables.add(dataTable);
            }
        }
    }

    /**
     * 普通对比
     * @param configId
     * @param tables
     */
    public CompareDataTable(String configId, DataTable... tables)
    {
        this(configId, null, false, tables);
    }

    /**
     * 带有差异原因的对比
     * @param configId
     * @param diffReasonTable
     * @param transDiffCol2CompareCol 是否将差异原因的字段转成对比字段
     * @param tables
     */
    public CompareDataTable(String configId, DataTable diffReasonTable, boolean transDiffCol2CompareCol,
            DataTable... tables)
    {
        super();
        this.configId = configId;
        this.diffReasonTable = diffReasonTable;
        this.transDiffCol2CompareCol = transDiffCol2CompareCol;
        this.tables = new ArrayList<DataTable>();
        if (tables != null && tables.length > 0)
        {
            for (DataTable dataTable : tables)
            {
                this.tables.add(dataTable);
            }
        }
    }

    protected CompareDataTable()
    {
    }

    /**
     * 给当前对比的TableConfig添加手动设置相等配置信息
     * @param valueOne
     * @param valueTwo
     * @throws Exception
     */
    public void addHandSetEquals(String valueOne, String valueTwo) throws Exception
    {
        loadTableConfig();
        if (tableConfig != null)
        {
            tableConfig.addHandSetEquals(valueOne, valueTwo);
            restoreCompareFlag();
        }
    }
    
    /**
     * 给指定的TableConfig添加手动设置相等配置信息
     * @param valueOne
     * @param valueTwo
     * @throws Exception
     */
    public void addHandSetEquals(String configId, String valueOne, String valueTwo) throws Exception
    {
        getAllCompareDataTableConfig();
        CompareDataTableConfig tableConfig = configMap == null ? null : configMap.get(configId);
        if (tableConfig != null)
        {
            tableConfig.addHandSetEquals(valueOne, valueTwo);
            restoreCompareFlag();
        }
    }
    
    /**
     * 添加合并配置
     * @param configId
     * @param combine
     * @throws Exception 
     */
    public void addCombine(Set<? extends CombineColumnConfig> combine) throws Exception
    {
        loadTableConfig();
        if (tableConfig != null)
        {
            if (combineMap == null)
            {
                combineMap = new HashMap<String, Set<? extends CombineColumnConfig>>();
            }
            combineMap.put(tableConfig.getId(), combine);
        }
    }
    
    /**
     * 给指定id添加合并配置
     * @param configId
     * @param combine
     */
    public void addCombine(String configId, Set<? extends CombineColumnConfig> combine)
    {
        if (combineMap == null)
        {
            combineMap = new HashMap<String, Set<? extends CombineColumnConfig>>();
        }
        combineMap.put(configId, combine);
    }

    /**
     * 把当前的树生成DataTable
     * @throws Exception
     */
    public void changeCurrentTree2DataTable() throws Exception
    {
        try
        {
            handleSameNameTables(tables);
            resultTable = resultTree == null ? null : resultTree.toDataTable();
        } finally 
        {
            // 把替换后的DataName替换回来
            reHandleSameNameTables(tables);
        }
    }

    /**
     * configId值变(用于对比时tab页切换)
     * @param configId
     * @throws Exception
     */
    public void configIdChange(String configId) throws Exception
    {
        // 如果configId和当前的configId一致，则不触发configId值变
        if (configId.equals(this.configId))
        {
            return;
        }
        this.configId = configId;
        tableConfig = null;
        tables = null;
        loadTableConfig();
        // 对比标记值为false
        restoreCompareFlag();
    }

    /**
     * 差异原因DataTable值变
     * @param diffReasonTable
     * @throws Exception
     */
    public void diffReasonTableChange(DataTable diffReasonTable) throws Exception
    {
        diffReasonTableChange(diffReasonTable, false);
    }

    /**
     * 差异原因DataTable值变
     * @param diffReasonTable
     * @param transDiffCol2CompareCol 是否将差异原因的字段转成对比字段
     * @throws Exception
     */
    public void diffReasonTableChange(DataTable diffReasonTable, boolean transDiffCol2CompareCol) throws Exception
    {
        this.diffReasonTable = diffReasonTable;
        this.transDiffCol2CompareCol = transDiffCol2CompareCol;
        diffMatchNodes = null;
        if (!CoreUtil.checkDataTableHasData(diffReasonTable))
        {
            return;
        }
        doCompare();
        // 处理差异原因
        if (tableConfig.isHasDiffReason())
        {
            addDiffReasonTable();
            // 重新生成DataTable
            resultTable = resultTree == null ? null : resultTree.toDataTable();
        }
    }

    /**
     * 导出对比结果
     * @return
     * @throws Exception
     */
    public byte[] exportCompareResult() throws Exception
    {
        DataSet2Excel dataSet2Excel = getExporter();
        Assert.isNotNull(dataSet2Excel, "获取导出者失败！");
        return dataSet2Excel.exportExcel();
    }

    /**
     * 获取所有的对比配置
     * @return
     * @throws Exception
     */
    public List<CompareDataTableConfig> getAllCompareDataTableConfig() throws Exception
    {
        loadTableConfig();
        if (configMap == null || configMap.isEmpty())
        {
            return null;
        }
        return new ArrayList<CompareDataTableConfig>(configMap.values());
    }

    /**
     * 导出所有对比结果
     * @return
     * @throws Exception
     */
    public DataTables getAllCompareResult() throws Exception
    {
        multiOperate = true;
        try
        {
            doCompare();
        } finally
        {
            multiOperate = false;
        }
        if (resultTables == null || resultTables.isEmpty())
        {
            DataTable table = getCompareResult();
            if (table != null)
            {
                DataTables result = new DataTables();
                result.add(table);
                return result;
            } else
            {
                return null;
            }
        }
        DataTables result = new DataTables();
        for (Entry<CompareDataTableConfig, DataTable> entry : resultTables.entrySet())
        {
            DataTable table = entry.getValue();
            DataTable singleTable = CoreUtil.copyDataTable(table, true);
            if (singleTable == null)
            {
                continue;
            }
            replaceSameName(singleTable, sameNameCache);
            result.add(singleTable);
        }
        return result;
    }

    /**
     * 获取和当前对比结果结构一致的差异原因DataTable 如果构建时已经传进来了DataTable，也会将传进来的差异原因填充到结果中
     * @return
     * @throws Exception
     */
    public DataTable getCompareDiffReasonTable() throws Exception
    {
        return getCompareDiffReasonTable(false);
    }

    /**
     * 获取和当前对比结果结构一致的差异原因DataTable 如果构建时已经传进来了DataTable，也会将传进来的差异原因填充到结果中
     * @param addUuIdCol 是否添加唯一标识列
     * @return
     * @throws Exception
     */
    public DataTable getCompareDiffReasonTable(boolean addUuIdCol) throws Exception
    {
        doCompare();
        // 没有对比结果直接返回null
        if (resultTree == null)
        {
            return null;
        }
        DataTable result = new DataTable();
        if (addUuIdCol)
        {
            result.addNewColumn(CompareDataTable.差异原因唯一标识);
        }
        // 构建表结构
        // 使用tableConfig中的结构构建差异原因
        Map<String, String> columns = tableConfig.getAllMatchAndDiffColumnName();
        if (!CoreUtil.isEmpty(columns))
        {
            for (String string : columns.values())
            {
                result.addNewColumn(string);
            }
        }
        DataColumn newCol = new DataColumn(tableConfig.getDiffColumnName(), tableConfig.getDiffColumnLabel());
        result.getColumns().add(newCol);
        List<CompareTreeNode> allNodes = resultTree.recursionGetTreeNodes();
        if (CoreUtil.isEmpty(allNodes))
        {
            return result;
        }
        // 填充表数据
        DataRow newRow = null;
        for (CompareTreeNode compareTreeNode : allNodes)
        {
            newRow = result.newRow();
            if (!CoreUtil.isEmpty(columns))
            {
                for (Entry<String, String> entry : columns.entrySet())
                {
                    newRow.getItemMap().put(entry.getValue(),
                            compareTreeNode.getSource().getItemMap().get(entry.getKey()));
                }
            }
            // 传进来的diffReasoonTable中的差异原因
            if (diffMatchNodes != null && diffMatchNodes.containsKey(compareTreeNode))
            {
                newRow.getItemMap().put(tableConfig.getDiffColumnName(),
                        diffMatchNodes.get(compareTreeNode).getSource().getValue(tableConfig.getDiffColumnName()));
            }
            if (addUuIdCol)
            {
                newRow.getItemMap().put(CompareDataTable.差异原因唯一标识, CoreUtil.newUuId());
            }
            result.getRows().add(newRow);
        }
        return result;
    }

    public DataTable getCompareResult() throws Exception
    {
        doCompare();
        if (resultTable == null)
        {
            return resultTable;
        }
        DataTable result = CoreUtil.copyDataTable(resultTable, true);
        replaceSameName(result, sameNameCache);
        return result;
    }

    /**
     * 获取合并对比结果
     * @return
     */
    public DataTable getCompareResult(String configId) throws Exception
    {
        configIdChange(configId);
        return getCompareResult();
    }

    public String getCompareResultAsHtml() throws Exception
    {
        doCompare();
        return new DataTable2Html.Builder().setDataTable(resultTable).setSameNameCache(sameNameCache)
                .setSplitFlag(ExcelHtmlUtil.REPLACEPOINTFLAG).build().createHtml();
    }

    /**
     * 获取合并对比结果
     * @return
     */
    public String getCompareResultAsHtmlAndLeftRight(String configId) throws Exception
    {
        configIdChange(configId);
        return getCompareResultAsHtmlAndLeftRight();
    }
    
    public String getCompareResultAsHtmlAndLeftRight() throws Exception
    {
        doCompare();
        StringBuilder result = new StringBuilder();
        result.append("<div class='tableleftright'><span class='tableleft'>").append(resultTable.getLeftHeader()).append("</span>").append("<span class='tableright'>").append(resultTable.getRightHeader()).append("</span>").append("</div>");
        result.append(new DataTable2Html.Builder().setDataTable(resultTable).setSameNameCache(sameNameCache)
                .setSplitFlag(ExcelHtmlUtil.REPLACEPOINTFLAG).build().createHtml());
        return result.toString();
    }

    /**
     * 获取合并对比结果
     * @return
     */
    public String getCompareResultAsHtml(String configId) throws Exception
    {
        configIdChange(configId);
        return getCompareResultAsHtml();
    }
    

    public AbstractCompareTree getCompareResultAsTree() throws Exception
    {
        doCompare();
        return resultTree;
    }

    /**
     * 获取统计结果树
     * @return
     * @throws Exception
     */
    public AbstractCompareTree getCompareResultAsTree(String configId) throws Exception
    {
        configIdChange(configId);
        return getCompareResultAsTree();
    }

    /**
     * 获取当前对比的报表对比配置
     * @return
     * @throws Exception
     */
    public CompareDataTableConfig getCompareTableConfig() throws Exception
    {
        loadTableConfig();
        return tableConfig;
    }

    /**
     * 获取导出者
     * @return
     * @throws Exception
     */
    public DataSet2Excel getExporter() throws Exception
    {
        multiOperate = true;
        try
        {
            doCompare();
        } finally
        {
            multiOperate = false;
        }
        // 该接口也兼容批量导出
        Map<CompareDataTableConfig, DataTable> tables = new HashMap<CompareDataTableConfig, DataTable>();
        if (!CoreUtil.isEmpty(resultTables))
        {
            tables.putAll(resultTables);
        }
        if (resultTable != null)
        {
            tables.put(tableConfig, resultTable);
        }
        if (CoreUtil.isEmpty(tables))
        {
            return null;
        }
        Map<CompareDataTableConfig, DataColumn> removeCols = new HashMap<CompareDataTableConfig, DataColumn>();
        DataSet2Excel result = null;
        try
        {
            // 合并配置
            Map<String, Set<? extends CombineColumnConfig>> combineMap = new HashMap<String, Set<? extends CombineColumnConfig>>();
            for (Entry<CompareDataTableConfig, DataTable> entry : tables.entrySet())
            {
                DataTable resultTable = entry.getValue();
                // 对比带有差异原因的比较需要移除最后一个分析列
                if (entry.getKey().isHasDiffReason() && CoreUtil.checkDataTableHasColumns(resultTable))
                {
                    DataColumn removeCol = resultTable.getColumns().remove(resultTable.getColumns().size() - 1);
                    // 还需要判断移除列的合法性
                    if (!CompareDataTable.操作.equals(removeCol.getColumnName()))
                    {
                        resultTable.getColumns().add(removeCol);
                    } else
                    {
                        removeCols.put(entry.getKey(), removeCol);
                    }
                }
                if (this.combineMap.containsKey(entry.getKey().getId()))
                {
                    combineMap.put(resultTable.getTableName(), this.combineMap.get(entry.getKey().getId()));
                }
            }
            DataTables ds = new DataTables();
            ds.addAll(resultTables.values());
            result = new DataSet2Excel.Builder().setCombineMap(combineMap).setDataSet(ds).setSameNameCache(sameNameCache).setRecalWidth(true)
                    .setSplitFlag(ExcelHtmlUtil.REPLACEPOINTFLAG).build();
        } finally
        {
            // 还原移除的列
            if (!removeCols.isEmpty())
            {
                for (Entry<CompareDataTableConfig, DataTable> entry : resultTables.entrySet())
                {
                    if (removeCols.containsKey(entry.getKey()))
                    {
                        entry.getValue().getColumns().add(removeCols.get(entry.getKey()));
                    }
                }
            }
        }
        return result;
    }

    /**
     * 获取左页眉
     * @return
     * @throws Exception
     */
    public String getLeft() throws Exception
    {
        getCompareResultAsTree();
        return tableConfig == null ? "" : tableConfig.getLeft();
    }

    /**
     * 获取右页眉
     * @return
     * @throws Exception
     */
    public String getRight() throws Exception
    {
        getCompareResultAsTree();
        return tableConfig == null ? "" : tableConfig.getRight();
    }

    /**
     * 获取当前对比的DataTables
     * @return
     */
    public List<DataTable> getTables()
    {
        return tables;
    }

    /**
     * 重置handSetEquals
     * @param handSetEquals
     * @throws Exception
     */
    public void handSetEqualsChange(Map<String, Set<String>> handSetEquals) throws Exception
    {
        loadTableConfig();
        if (tableConfig != null)
        {
            tableConfig.setHandSetEquals(handSetEquals);
            restoreCompareFlag();
        }
    }

    /**
     * 标注值变
     * @param labelLevel
     * @throws Exception
     */
    public void labelLevelChange(double labelLevel) throws Exception
    {
        loadTableConfig();
        if (tableConfig != null)
        {
            tableConfig.setLabelLevel(labelLevel);
            // 重新生成resultTale即可
            resultTable = resultTree == null ? null : resultTree.toDataTable();
        }
    }

    /**
     * 还原对比标记
     */
    public void restoreCompareFlag()
    {
        hasCompare = false;
    }

    /**
     * 注入对比实现
     */
    public void setCompareTree(String configId, AbstractCompareTree tree)
    {
        if (trees == null)
        {
            trees = new HashMap<String, List<AbstractCompareTree>>();
        }
        List<AbstractCompareTree> list = trees.get(configId);
        if (list == null)
        {
            list = new ArrayList<AbstractCompareTree>();
            trees.put(configId, list);
        }
        list.add(tree);
        trees.put(configId, list);
    }

    public void setConfigId(String configId)
    {
        this.configId = configId;
        restoreCompareFlag();
    }

    public void setDiffReasonTable(DataTable diffReasonTable)
    {
        setDiffReasonTable(diffReasonTable, false);
    }

    public void setDiffReasonTable(DataTable diffReasonTable, boolean transDiffCol2CompareCol)
    {
        this.diffReasonTable = diffReasonTable;
        this.transDiffCol2CompareCol = transDiffCol2CompareCol;
        restoreCompareFlag();
    }

    public void setDiffReasonTableMap(Map<String, DataTable> diffReasonTableMap)
    {
        setDiffReasonTableMap(diffReasonTableMap, false);
    }

    public void setDiffReasonTableMap(Map<String, DataTable> diffReasonTableMap, boolean transDiffCol2CompareCol)
    {
        this.diffReasonTableMap = diffReasonTableMap;
        this.transDiffCol2CompareCol = transDiffCol2CompareCol;
        restoreCompareFlag();
    }

    /**
     * 重置handSetEquals
     * @param configId
     * @param handSetEquals
     * @throws Exception
     */
    public void setHandSetEquals(String configId, Map<String, Set<String>> handSetEquals) throws Exception
    {
        getAllCompareDataTableConfig();
        CompareDataTableConfig tableConfig = configMap == null ? null : configMap.get(configId);
        if (tableConfig != null)
        {
            tableConfig.setHandSetEquals(handSetEquals);
            restoreCompareFlag();
        }
    }

    public void setIn(InputStream in) throws Exception
    {
        this.in = in;
        // 重新读取配置
        configMap = CompareDataTableConfigApp.load(in);
        tableConfig = null;
        restoreCompareFlag();
    }

    /**
     * 设置标注级别
     * @param configId
     * @param labelLevel
     * @throws Exception
     */
    public void setLabelLevel(String configId, double labelLevel) throws Exception
    {
        getAllCompareDataTableConfig();
        CompareDataTableConfig tableConfig = configMap == null ? null : configMap.get(configId);
        if (tableConfig != null)
        {
            tableConfig.setLabelLevel(labelLevel);
            restoreCompareFlag();
        }
    }

    /**
     * 设置相似度
     * @param configId 对应的配置ID
     * @param similarity
     * @param similarityEnum
     * @throws Exception
     */
    public void setSimilarity(String configId, int similarity, SimilarityEnum similarityEnum) throws Exception
    {
        getAllCompareDataTableConfig();
        CompareDataTableConfig tableConfig = configMap == null ? null : configMap.get(configId);
        if (tableConfig != null)
        {
            switch (similarityEnum)
            {
            case EQUALSSIMILARITY:
                tableConfig.getEqualsColumnConfig().setSimilarity(similarity);
                break;
            case OTHERCOLSIMILARITY:
                tableConfig.getEqualsColumnConfig().setOtherColSimilarity(similarity);
                break;
            case ADDITIVECOLSIMILARITY:
                tableConfig.getEqualsColumnConfig().setAdditiveColSimilarity(similarity);
                break;
            default:
                break;
            }
            restoreCompareFlag();
        }
    }

    public void setTableConfig(CompareDataTableConfig tableConfig)
    {
        this.tableConfig = tableConfig;
        restoreCompareFlag();
    }

    public void setTableMap(Map<String, List<DataTable>> tableMap)
    {
        this.tableMap = tableMap;
        restoreCompareFlag();
    }

    public void setTables(DataTable... tables)
    {
        this.tables = Arrays.asList(tables);
        restoreCompareFlag();
    }

    /**
     * 当前相似度值变
     * @param similarity
     * @param similarityEnum
     * @throws Exception
     */
    public void similarityChange(int similarity, SimilarityEnum similarityEnum) throws Exception
    {
        tableConfig = loadTableConfig();
        if (tableConfig != null)
        {
            switch (similarityEnum)
            {
            case EQUALSSIMILARITY:
                tableConfig.getEqualsColumnConfig().setSimilarity(similarity);
                break;
            case OTHERCOLSIMILARITY:
                tableConfig.getEqualsColumnConfig().setOtherColSimilarity(similarity);
                break;
            case ADDITIVECOLSIMILARITY:
                tableConfig.getEqualsColumnConfig().setAdditiveColSimilarity(similarity);
                break;
            default:
                break;
            }
            // 设置为没有对比
            restoreCompareFlag();
        }
    }

    /**
     * 对比tables值变
     * @param tables
     */
    public void tablesChange(DataTable... tables)
    {
        setTables(tables);
    }

    /**
     * 添加差异原因
     * @throws Exception
     */
    private void addDiffReasonTable() throws Exception
    {
        if (resultTree == null || !CoreUtil.checkDataTableHasData(diffReasonTable))
        {
            return;
        }
        // 递归获取子节点
        List<CompareTreeNode> allChilds = resultTree.recursionGetTreeNodes();
        if (allChilds == null || allChilds.isEmpty())
        {
            return;
        }
        AbstractCompareTree diffTree = getTreeImplByConfig(tableConfig);
        // 是否需要将差异原因字段转成对比字段
        if (transDiffCol2CompareCol)
        {
            DataTable transTable = transDiffCol2CompareCol(diffReasonTable);
            diffTree.setSource(transTable);
        } else
        {
            diffTree.setSource(diffReasonTable);
        }
        diffTree.build();
        diffMatchNodes = resultTree.getMatchTreeNodes(diffTree);
        if (CoreUtil.isEmpty(diffMatchNodes))
        {
            return;
        }
        for (CompareTreeNode compareTreeNode : allChilds)
        {
            CompareTreeNode matchNode = diffMatchNodes.get(compareTreeNode);
            if (matchNode == null)
            {
                continue;
            }
            compareTreeNode.setDiffReason(
                    CoreUtil.parseStr(matchNode.getSource().getItemMap().get(tableConfig.getDiffColumnName())));
        }
    }
    
    /**
     * 执行对比
     * @throws Exception
     */
    private void doCompare() throws Exception
    {
        if (hasCompare)
        {
            return;
        }
        if (tableMap != null && !tableMap.isEmpty() && multiOperate)
        {
            multiCompare();
        } else
        {
            singleCompare();
        }
    }
    

    /**
     * 单对比实现
     * @throws Exception
     */
    private void singleCompare() throws Exception
    {
        tableConfig = loadTableConfig();
        Assert.isNotNull(tableConfig, "获取" + configId + "的对比合并配置失败！");
        compareImpl();
        hasCompare = true;
    }

    private void compareImpl() throws Exception
    {
        if (tables == null || tables.isEmpty())
        {
            // 尝试从tablemap中获取数据
            tables = tableMap == null ? null : tableMap.get(tableConfig.getId());
        }
        if (tables == null || tables.isEmpty())
        {
            return;
        }
        List<AbstractCompareTree> compareTrees = new ArrayList<AbstractCompareTree>();
        for (DataTable table : tables)
        {
            Assert.isNotNull(table, tableConfig.getId() + "配置下存在空的DataTable！");
            Assert.isNotNullOrEmpty(table.getTableName(), tableConfig.getId() + "配置下存在DataTableName为空的DataTable！");
            AbstractCompareTree tree = getTreeImplByConfig(tableConfig);
            // 2018 04 11 
            if (tableConfig.getTransRowColConfig() != null)
            {
                // 行列转换
                table = transRowCol(table);
            }
            tree.setSource(table);
            tree.build();
            compareTrees.add(tree);
        }
        try
        {
            // 存在对比列和查看列的时候才需要考虑同名问题
            handleSameNameTables(tables);
            CompareTree compareTree = new CompareTree(tableConfig, compareTrees);
            resultTree = compareTree.getCompareResultAsTree();
            // 处理差异原因
            if (tableConfig.isHasDiffReason())
            {
                addDiffReasonTable();
            }
            changeCurrentTree2DataTable();
        } finally
        {
            // 把替换后的DataName替换回来
            reHandleSameNameTables(tables);
        }
    }

    /**
     * 获取列配置对应的列（兼容列配置）
     * @param table
     * @param columnConfig
     * @return
     */
    private DataColumn getDataColumnByColumnConfig(DataTable table, ColumnConfig columnConfig)
    {
        DataColumn result = table.getColumn(columnConfig.getColumnName());
        if (result == null && !CoreUtil.isEmpty(columnConfig.getCompatibleColumnNames()))
        {
            // 获取兼容列
            for (String columnName : columnConfig.getCompatibleColumnNames())
            {
                result = table.getColumn(columnName);
                if (result != null)
                {
                    break;
                }
            }
        }
        return result;
    }

    /**
     * 获取原始的对比结果，没有处理同名问题的结果 批量导出时使用
     * @return
     * @throws Exception
     */
    private DataTable getOriginalCompareResult() throws Exception
    {
        doCompare();
        return resultTable;
    }

    /**
     * 获取对比实现的树
     * @param config
     * @return
     * @throws Exception
     */
    private AbstractCompareTree getTreeImplByConfig(CompareDataTableConfig config) throws Exception
    {
        if (trees != null && trees.containsKey(config.getId()))
        {
            AbstractCompareTree result = trees.get(config.getId()).remove(0);
            result.setTableConfig(config);
            return result;
        }
        return CompareTreeFactory.getTreeImplByConfig(tableConfig);
    }

    /**
     * 存在对比列和查看列的时候才处理DataTable的同名问题
     * @param trees
     */
    private void handleSameNameTables(List<DataTable> tables)
    {
        if (tables == null || tables.size() < 2)
        {
            return;
        }
        Set<String> names = new HashSet<String>();
        for (DataTable table : tables)
        {
            names.add(table.getTableName());
        }
        if (names.size() == tables.size())
        {
            return;
        }
        sameNameCache = new HashMap<String, String>();
        for (DataTable table : tables)
        {
            String uuId = CoreUtil.newUuId();
            sameNameCache.put(uuId, table.getTableName());
            table.setTableName(uuId);
        }
    }

    /**
     * 加载ableConfig
     * @return
     * @throws Exception
     */
    private CompareDataTableConfig loadTableConfig() throws Exception
    {
        // 2017 06 15 新增兼容注入文件流
        if (tableConfig != null)
        {
            return tableConfig;
        }
        if (in != null)
        {
            if (configMap == null || configMap.isEmpty())
            {
                configMap = CompareDataTableConfigApp.load(in);
            }
            tableConfig = configMap == null ? null : configMap.get(configId);
        } else
        {
            if (tableConfig == null)
            {
                tableConfig = CompareDataTableConfigApp.getCompareDataTableConfig(configId);
            }
            if (tableConfig != null)
            {
                configMap = new LinkedHashMap<String, CompareDataTableConfig>();
                configMap.put(tableConfig.getId(), tableConfig);
            }
        }
        return tableConfig;
    }

    /**
     * 多对比实现
     * @throws Exception
     */
    private void multiCompare() throws Exception
    {
        resultTables = new LinkedHashMap<CompareDataTableConfig, DataTable>();
        if (tableMap == null || tableMap.isEmpty())
        {
            DataTable table = getOriginalCompareResult();
            resultTables.put(loadTableConfig(), table);
        } else
        {
            loadTableConfig();
            Assert.isNotNullOrEmpty(configMap, "获取配置流的配置信息失败！");
            sameNameCache = new HashMap<String, String>();
            // 对比多个
            DataTable singletable = null;
            Map<String, String> singleSameNameCache = null;
            for (Entry<String, CompareDataTableConfig> entry : configMap.entrySet())
            {
                CompareDataTableConfig config = entry.getValue();
                // 没有对应配置则忽略
                if (config == null)
                {
                    continue;
                }
                CompareDataTable compare = new CompareDataTable(config,
                        diffReasonTableMap == null ? null : diffReasonTableMap.get(config.getId()),
                        transDiffCol2CompareCol,
                        tableMap.get(entry.getKey()) == null ? null
                                : tableMap.get(entry.getKey())
                                        .toArray(new DataTable[tableMap.get(entry.getKey()).size()]));
                // 注入手动注入的对比实现
                if (trees != null && trees.containsKey(config.getId()))
                {
                    int size = tableMap.get(entry.getKey()) == null ? 0 : tableMap.get(entry.getKey()).size();
                    for (int i = 0; i < size; i++)
                    {
                        compare.setCompareTree(config.getId(), trees.get(config.getId()).remove(0));
                    }
                }
                // 获取原始的对比结果
                singletable = compare.getOriginalCompareResult();
                singleSameNameCache = compare.sameNameCache;
                if (singleSameNameCache != null)
                {
                    sameNameCache.putAll(singleSameNameCache);
                }
                resultTables.put(config, singletable);
            }
        }
        hasCompare = true;
    }

    /**
     * 替换后的TableName替换回来
     * @param tables
     */
    private void reHandleSameNameTables(List<DataTable> tables)
    {
        if (tables == null || tables.size() < 2 || sameNameCache == null || sameNameCache.isEmpty())
        {
            return;
        }
        for (Entry<String, String> entry : sameNameCache.entrySet())
        {
            for (DataTable table : tables)
            {
                String tableName = table.getTableName();
                if (tableName != null && tableName.equals(entry.getKey()))
                {
                    table.setTableName(entry.getValue());
                }
            }
        }
    }

    /**
     * 替换同名问题
     * @param table
     * @param sameNameMap
     */
    private void replaceSameName(DataTable table, Map<String, String> sameNameMap)
    {
        if (table == null || sameNameMap == null || sameNameMap.isEmpty())
        {
            return;
        }
        // 处理同名问题
        if (sameNameMap != null && !sameNameMap.isEmpty())
        {
            sign: for (DataColumn dataColumn : table.getColumns())
            {
                for (Entry<String, String> entry : sameNameMap.entrySet())
                {
                    if (dataColumn.getColumnLable() != null && dataColumn.getColumnLable().contains(entry.getKey()))
                    {
                        dataColumn.setColumnLable(
                                StringUtil.replaceFirst(dataColumn.getColumnLable(), entry.getKey(), entry.getValue()));
                        continue sign;
                    }
                }
            }
        }
    }

    /**
     * 将差异原因的DataTable列名转成对比的列名
     * @param diffReasonTable
     * @return
     */
    private DataTable transDiffCol2CompareCol(DataTable diffReasonTable)
    {
        Map<String, String> matchCols = tableConfig.getAllMatchAndDiffColumnName();
        if (CoreUtil.isEmpty(matchCols))
        {
            return null;
        }
        DataTable result = new DataTable(diffReasonTable.getTableName());
        for (String key : matchCols.keySet())
        {
            result.addNewColumn(key);
        }
        result.addNewColumn(tableConfig.getDiffColumnName());
        DataRow newRow = null;
        for (DataRow row : diffReasonTable.getRows())
        {
            newRow = result.newRow();
            for (Entry<String, String> entry : matchCols.entrySet())
            {
                newRow.getItemMap().put(entry.getKey(), row.getItemMap().get(entry.getValue()));
            }
            // 差异原因值
            newRow.getItemMap().put(tableConfig.getDiffColumnName(),
                    row.getItemMap().get(tableConfig.getDiffColumnName()));
            result.getRows().add(newRow);
        }
        return result;
    }

    private DataTable transRowCol(DataTable table) throws Exception
    {
        TransRowColConfig rowCol = tableConfig.getTransRowColConfig();
        Remind.isNotNullOrEmpty(rowCol.getTransRowColName(), tableConfig.getId() + "下转换配置transRowColName不能为空！");
        List<TransColConfig> colConfigs = rowCol.getTransColConfigs();
        List<TransRowConfig> rowConfigs = rowCol.getTransRowConfigs();
        Remind.isTrue(!CoreUtil.isEmpty(colConfigs) && !CoreUtil.isEmpty(rowConfigs),
                tableConfig.getId() + "下的行列转换配置不正确，请检查配置！");
        DataTable result = new DataTable(table.getTableName());
        boolean addTblIndexCol = !CoreUtil.isEmpty(rowCol.getTblIndexColName());
        if (addTblIndexCol)
        {
            result.addNewColumn(rowCol.getTblIndexColName());
        }
        result.addNewColumn(rowCol.getTransRowColName());
        // 行变列
        List<TwoTuple<DataRow, DataColumn>> columns = new ArrayList<TwoTuple<DataRow, DataColumn>>();
        for (TransRowConfig rowConfig : rowConfigs)
        {
            DataColumn column = getDataColumnByColumnConfig(table, rowConfig);
            if (column == null)
            {
                continue;
            }
            Set<String> values = new HashSet<String>();
            values.add(rowConfig.getMatchValue());
            if (!CoreUtil.isEmpty(rowConfig.getCompatibleMatchValues()))
            {
                values.addAll(rowConfig.getCompatibleMatchValues());
            }
            for (DataRow row : table.getRows())
            {
                String value = CoreUtil.parseStr(row.getItemMap().get(column.getColumnName()));
                if (values.contains(value))
                {
                    columns.add(new TwoTuple<DataRow, DataColumn>(row, result.addNewColumn(value)));
                    break;
                }
            }
        }
        // 列变行
        for (TransColConfig colConfig : colConfigs)
        {
            DataColumn column = getDataColumnByColumnConfig(table, colConfig);
            if (column == null)
            {
                continue;
            }
            DataRow newRow = result.newRow();
            if (addTblIndexCol)
            {
                newRow.getItemMap().put(rowCol.getTblIndexColName(), colConfig.getTblIndex());
            }
            newRow.getItemMap().put(rowCol.getTransRowColName(),
                    CoreUtil.isEmpty(colConfig.getRenameColumnName()) ? column.getColumnLable()
                            : colConfig.getRenameColumnName());
            for (TwoTuple<DataRow, DataColumn> twoTuple : columns)
            {
                newRow.getItemMap().put(twoTuple.getB().getColumnName(),
                        twoTuple.getA().getItemMap().get(column.getColumnName()));
            }
            result.getRows().add(newRow);
        }
        return result;
    }

    public Map<String, String> getSameNameCache() throws Exception
    {
        multiOperate = true;
        try
        {
            doCompare();
        } finally
        {
            multiOperate = false;
        }
        return sameNameCache;
    }

    /**
     * 获取原始的那份对比结果
     * @return
     * @throws Exception
     */
    public DataTable getResultTable() throws Exception
    {
        doCompare();
        return resultTable;
    }

    /**
     * 获取原始的那份对比结果
     * @return
     * @throws Exception
     */
    public DataTables getResultTables() throws Exception
    {
        multiOperate = true;
        try
        {
            doCompare();
        } finally
        {
            multiOperate = false;
        }
        if (resultTables == null || resultTables.isEmpty())
        {
            if (resultTable != null)
            {
                DataTables result = new DataTables();
                result.add(resultTable);
                return result;
            } else
            {
                return null;
            }
        }
        DataTables result = new DataTables();
        result.addAll(resultTables.values());
        return result;
    }
    
}
